package deepbrain.iit;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import deepbrain.simnetwork.network.NetworkState;
import deepbrain.simnetwork.network.NodeState;

public class PHICompUtils {

	public static int[] masks = new int[32];
	private static double log2 = Math.log(2);

	static {
		for (int i = 0; i < 32; i++) {
			masks[i] = 1 << i;
		}
	}

	public static double getProbability2CertainState(NodeState[] stochastic,
			NodeState[] certainState, List<Integer> part) {
		double prob = 1.0;
		for (int i : part)
			prob *= stochastic[i].getProbability(certainState[i]
					.isCertainFiring());
		return prob;
	}

	public static double computeEI(Collection<Double> probDist, int numOfNodes) {
		double sigmaP = 0;
		double result = 0;
		for (Double prob : probDist) {
			sigmaP += prob;
			result += prob * PHICompUtils.log2(prob);
		}
		return numOfNodes + (result / sigmaP - log2(sigmaP));
	}

	public static double sumDoubleArray(double[] array) {
		double sum = 0.0;
		for (double d : array)
			sum += d;
		return sum;
	}

	public static void normalizeDoubleArray(double[] array) {
		double sum = sumDoubleArray(array);
		for (int i = 0; i < array.length; i++)
			array[i] /= sum;
	}

	public static double computeKLDiv(double[] p, double[] q) {
		if (p.length != q.length)
			throw new RuntimeException("len(prob1)!=len(prob2)");
		normalizeDoubleArray(p);
		normalizeDoubleArray(q);
		double dist = 0;
		for (int i = 0; i < p.length; i++) {
			if (p[i] != 0)
				dist += p[i] * log2(p[i] / q[i]);
		}
		return dist;
	}

	public static double computeKLDiv(List<Pair<Double, Double>> pairs) {
		double[] p = new double[pairs.size()];
		double[] q = new double[pairs.size()];
		for (int i = 0; i < pairs.size(); i++) {
			p[i] = pairs.get(i).u;
			q[i] = pairs.get(i).v;
		}
		return computeKLDiv(p, q);
	}

	public static double computeKLDiv(Pair<Double, Double>[] pairs) {
		double[] p = new double[pairs.length];
		double[] q = new double[pairs.length];
		for (int i = 0; i < pairs.length; i++) {
			p[i] = pairs[i].u;
			q[i] = pairs[i].v;
		}
		return computeKLDiv(p, q);
	}

	public static double log2(double p) {
		return Math.log(p) / log2;
	}

	public static double computeKLDiv(HashMap<NetworkState, Double> pX0X1,
			HashMap<NetworkState, Double> pM0M1) {
		double sumP = 0, sumQ = 0;
		for (Double p:pX0X1.values()) {
			sumP += p;
		}
		
		for (Double q:pM0M1.values()) {
			sumQ += q;
		}

		double dist = 0;
		for (Map.Entry<NetworkState, Double> entry:pX0X1.entrySet()) {
			double p = entry.getValue() / sumP;
			double q = pM0M1.get(entry.getKey()) / sumQ;
			if (p != 0)
				dist += p * log2(p / q);
		}
		return dist;

	}
}
